﻿
<div class="container-wrapper"
     onmouseover="formDesigner.highlihter.activate(event, @(ComponentHighlighterCssClass))">
    <div class="widget-wrapper"
         @onclick="@OnClickCallback"
         @onclick:preventDefault="true"
         @onclick:stopPropagation="true"
         onmouseout="formDesigner.highlihter.deactivate(event, @(ComponentHighlighterCssClass))">
        @if (ContainerData != null)
        {
            if (ContainerData.Rows != null)
            {
                @for (int rowIndex = 0; rowIndex < ContainerData?.Rows.Count; rowIndex++)
                {
                    var currentRowIndex = rowIndex;
                    var currentRow = ContainerData.Rows[currentRowIndex];
                    //如果为第一行,添加虚拟框div,后期这里应该加一个条件是设计模式在才会有
                    @if (currentRowIndex == 0 && !ContainerData.IsEmpty())//当拖动到页面的最上面的时候,会触发伪类元素生成
                    {
                        Guid divId = Guid.NewGuid();
                        <div id="@divId"
                             class="row dropRow"
                             ondragover="event.preventDefault();"
                             ondragenter="event.preventDefault();formDesigner.utils.addClass(this, @(DropZoneCssClasses))"
                             ondragleave="event.preventDefault();formDesigner.utils.removeClass(this, @(DropZoneCssClasses))"
                             @ondrop="@(async (e) =>
                                {
                                    await DropComponentBeforeRowAsync(currentRowIndex, ContainerData);
                                    await OnDropFinishedAsync(divId);
                                })"
                             @ondrop:preventDefault="true">
                        </div>
                    }

                    //正式解析
                    bool isMoveRowUpVisible = IsMoveRowUpVisible(currentRow);
                    bool isMoveRowDownVisible = IsMoveRowDownVisible(currentRow);
                    bool isRowElement = isMoveRowUpVisible ||
                    isMoveRowDownVisible ||
                    ContainerData?.Rows.Count > 0;
                    string rowElementClass = isRowElement ? "row-element" : "";
                    string rowSeparatorClass = currentRowIndex == ContainerData?.Rows.Count - 1 ?
                    "row-separator" : "";

                    <div class="row row-builder @rowElementClass @rowSeparatorClass"
                         @onclick="async(e)=>{
                     await Root.SelectRowAsync(currentRow);
                 }"
                         @onclick:stopPropagation="true">

                        @if (isRowElement)//添加行左右移动的功能
                        {
                            <div class="component-caption component-caption--row">
                              

                                @if (isMoveRowUpVisible)
                                {
                           
                                    <Button OnClick="@(async () => await MoveRowUpAsync(currentRow))" Size="Size.Small" Icon="fas fa-circle-arrow-up">
                                    </Button>
                                }

                                @if (isMoveRowDownVisible)
                                {
                                   
                                    <Button OnClick="@(async () => await MoveRowDownAsync(currentRow))" Size="Size.Small" Icon="fas fa-circle-arrow-down">
                                    </Button>

                                }

                            </div>
                        }

                        @for (int componentIndex = 0; componentIndex < currentRow.ComponentList.Count; componentIndex++)
                        {
                            var currentComponentIndex = componentIndex;
                            var currentComponent = currentRow.ComponentList[componentIndex];
                            string componentColumnWidth =
                            ComponentUtils.GetComponentColumnCssClasses(currentComponent);
                            string h = "auto";
                            h = currentComponent?.Height <= 0 ? "auto" : currentComponent?.Height + "px";
                            @if (currentComponent!.IsAbsPosition)
                            {
                                //z-index:@(currentComponent.Zindex)
                                <div class="draggable" style="position:absolute;top:@(currentComponent.Top)px;left:@(currentComponent.Left)px;width:@h;"
                                     draggable="true"
                                     @ondragstart="@(async () =>
                                         {
                                             await DragComponentStartAsync(currentComponent, currentRow, ContainerData);
                                         })">
                                    <CascadingValue Value="this" Name="Container">
                                        <Component ComponentData="@currentComponent"
                                                   ComponentRow="@currentRow"
                                                   ComponentIndex="@currentComponentIndex" />
                                    </CascadingValue>
                                </div>
                            }
                            else
                            {
                                <div class="draggable  @componentColumnWidth"
                                     draggable="true"
                                     @ondragstart="@(async () =>
                                         {
                                             await DragComponentStartAsync(currentComponent, currentRow, ContainerData);
                                         })">
                                    <CascadingValue Value="this" Name="Container">
                                        <Component ComponentData="@currentComponent"
                                                   ComponentRow="@currentRow"
                                                   ComponentIndex="@currentComponentIndex" />
                                    </CascadingValue>
                                </div>
                            }
                        }

                        @{
                            int rowSize = CalculateRowSize(currentRow);
                        }

                        @if (rowSize < ComponentUtils.MaxColumnWidth)
                        {
                            int columnSize = ComponentUtils.MaxColumnWidth - rowSize;
                            Guid divId = Guid.NewGuid();
                            <div id="@divId"
                                 class="widget-placeholder drop-container col-@columnSize d-flex justify-content-center text-center"
                                 data-col="@columnSize"
                                 ondragover="event.preventDefault();"
                                 ondragenter="event.preventDefault();formDesigner.utils.addClass(this, @DropZoneCssClasses)"
                                 ondragleave="event.preventDefault();formDesigner.utils.removeClass(this, @DropZoneCssClasses)"
                                 @ondrop="@(async (e) =>
                                    {
                                        await DropComponentToEndOfRowAsync(ContainerData, currentRow);
                                        await OnDropFinishedAsync(divId);
                                    })"
                                 @ondrop:preventDefault="true">
                            </div>
                        }

                    </div>
                    //如果不是空容器,那么就在最后一行加一个隐藏div,设计模式在加
                    @if (!ContainerData!.IsEmpty())
                    {
                        Guid divId = Guid.NewGuid();
                        var isLastDropRow = currentRowIndex == ContainerData?.Rows.Count - 1 ? "dropRow--last" : "";
                        <div id="@divId"
                             class="row dropRow @isLastDropRow"
                             ondragover="event.preventDefault();"
                             ondragenter="event.preventDefault();formDesigner.utils.addClass(this, 'bo-dropzone-hover bo-drag-enter')"
                             ondragleave="event.preventDefault();formDesigner.utils.removeClass(this, 'bo-dropzone-hover bo-drag-enter')"
                             @ondrop="@(async (e) =>
                                {
                                    await DropComponentAfterRowAsync(currentRowIndex, ContainerData);
                                    await OnDropFinishedAsync(divId);
                                })"
                             @ondrop:preventDefault="true">
                        </div>
                    }
                }
            }
        }
    </div>
</div>

@code
{
    /// <summary>
    /// 是否处于设计设计模式
    /// </summary>
    [Parameter]
    public bool IsDesigner { get; set; }
    /// <summary>
    /// 每一个组件都应该有这个属性
    /// </summary>
    [CascadingParameter(Name = "Root")]
    [NotNull]
    public FormDesigner? Root { get; set; }
    [Inject]
    [NotNull]
    public IJSRuntime? JSRuntime { get; set; }
    /// <summary>
    /// SelectContainerAsync();赋值当前选中的容器,早期逻辑,后期应该可以优化掉
    /// </summary>
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
    [Parameter]
    public EventCallback StateHasChangedOnContainer { get; set; }
    /// <summary>
    /// 容器本身的数据数据
    /// </summary>
    [Parameter]
    [NotNull]
    public ContainerDto? ContainerData { get; set; }

    private const string ComponentHighlighterCssClass = @"'component-element--hover'";
    private const string DropZoneCssClasses = @"'bo-dropzone-hover bo-drag-enter'";
    private const string DropZoneCssClassesWithoutSingleQuotes = @"bo-dropzone-hover bo-drag-enter";
    /// <summary>
    /// 设置本组件为FormDesigner中被选中的控件,并且刷新界面
    /// </summary>
    /// <returns></returns>
    private async void SelectRowAsync(RowDto row)
    {
        await Root.SelectRowAsync(row);
    }
    #region Move Row Up-Down Methods
    private bool IsMoveRowUpVisible(RowDto? componentsInRow)
    {
        //List<RowDto>
        return ContainerData.Rows.IsMoveLeftPossible(componentsInRow);
    }

    private bool IsMoveRowDownVisible(RowDto componentsInRow)
    {
        return ContainerData.Rows.IsMoveRightPossible(componentsInRow);
    }

    private async Task MoveRowUpAsync(RowDto componentsInRow)
    {
        ContainerData.Rows.MoveLeft(componentsInRow);
        await Task.CompletedTask;
    }

    private async Task MoveRowDownAsync(RowDto componentsInRow)
    {
        ContainerData.Rows.MoveRight(componentsInRow);
        await Task.CompletedTask;
    }

    #endregion Move Row Up-Down Methods
    #region Drag and Drop Methods
    /// <summary>
    /// 在设计器中拖动组件的时候
    /// </summary>
    /// <param name="draggedItemData">被拖动的组件</param>
    /// <param name="currentRow">被拖动组件所在的行</param>
    /// <param name="currentContainer">被拖动组件所在的容器</param>
    /// <returns></returns>
    private async Task DragComponentStartAsync(ComponentDto draggedItemData, RowDto currentRow, ContainerDto currentContainer)
    {
        await Root.SetDraggedComponentAsync(draggedItemData, currentRow, currentContainer);
    }

    /// <summary>
    /// 在拖动一个控件,并且要放到一个新行的时候(前置伪元素)
    /// </summary>
    /// <param name = "rowIndex"></param>
    /// <param name = "currentContainer"></param>
    /// <returns></returns>
    private async Task DropComponentBeforeRowAsync(int rowIndex, ContainerDto currentContainer)
    {
        var newRow = new RowDto();
        currentContainer.Rows.Insert(rowIndex, newRow);
        await DropComponentToEndOfRowAsync(currentContainer, newRow);
    }

    /// <summary>
    /// 在拖动一个控件,并且要放到一个新行的时候(后置伪元素),会new一个新的行出来,并Insert到Rows中
    /// </summary>
    /// <param name = "rowIndex"></param>
    /// <param name = "currentContainer"></param>
    /// <returns></returns>
    private async Task DropComponentAfterRowAsync(int rowIndex, ContainerDto? currentContainer)
    {
        var newRow = new RowDto();
        var newRowIndex = rowIndex + 1;
        currentContainer.Rows.Insert(newRowIndex, newRow);
        await DropComponentToEndOfRowAsync(currentContainer, newRow);
    }

    /// <summary>
    /// 拖拽一个组件到一个已经存在的行的时候触发
    /// </summary>
    /// <param name = "containerData"></param>
    /// <param name = "destinationRow"></param>
    /// <returns></returns>
    /// <exception cref = "NotImplementedException"></exception>
    private async Task DropComponentToEndOfRowAsync(ContainerDto containerData, RowDto destinationRow)
    {
        if (Root.IsDraggedItemPaletteWidget()) //从工具箱中拖动的
        {
            // you are trying to drag and drop a new widget from Palette
            var paletteWidgetData = await Root.GetDraggedPaletteWidgetAsync();
            var newComponentData = paletteWidgetData?.CreateComponent();
            await AddComponentToRowAsync(newComponentData, destinationRow);
            await Root.SelectComponentAsync(newComponentData);
        }
        else if (Root.IsDraggedItemComponent())
        {
            // you are trying to drag and drop a widget already defined in whiteboard
            var componentData = await Root.GetDraggedComponentDataAsync();
            var originRow = await Root.GetDraggedComponentOriginRowAsync();
            // de-attach widget from its previous position in origin row
            await DetachComponentFromPreviousRowAsync(componentData, originRow);
            // TODO: Check is component movable
            // add component to destination row
            await AddComponentToRowAsync(componentData, destinationRow);
            if (originRow.ComponentList.Count == 0)
            {
                var originContainer = await Root.GetDraggedComponentOriginContainerAsync();
                originContainer.Rows.Remove(originRow);
                originRow = null;
            }

            await Root.SelectComponentAsync(componentData);
        }
        else
        {
            throw new NotImplementedException();
        }
    }

    /// <summary>
    /// 移除拖动添加控件的样式
    /// </summary>
    /// <param name = "elementId"></param>
    /// <returns></returns>
    private async Task OnDropFinishedAsync(Guid elementId)
    {
        await JSRuntime.InvokeVoidAsync("formDesigner.utils.removeClassById", elementId, DropZoneCssClassesWithoutSingleQuotes);
    }

    #endregion Drag and Drop Methods
    #region Helper Methods
    /// <summary>
    /// 将组件添加到行中,并重新计算空间
    /// </summary>
    /// <param name = "componentData"></param>
    /// <param name = "currentRow"></param>
    /// <returns></returns>
    private async Task AddComponentToRowAsync(ComponentDto componentData, RowDto currentRow)
    {
        currentRow.ComponentList.Add(componentData);
        ComponentUtils.ComputeEachItemSizeInRow(currentRow);
        await Task.CompletedTask;
    }

    private async Task DetachComponentFromPreviousRowAsync(ComponentDto componentData, RowDto destinationRow)
    {
        var originRow = await Root.GetDraggedComponentOriginRowAsync();
        // remove component from origin row
        originRow.ComponentList.Remove(componentData);
        var originContainer = await Root.GetDraggedComponentOriginContainerAsync();
        if (destinationRow?.ComponentList.Count == 0 && destinationRow != originRow)
        {
            await originContainer.RemoveRowAsync(originRow);
        }

        if (originRow.ComponentList.Count == 0 && destinationRow != originRow)
        {
            await originContainer.RemoveRowAsync(originRow);
        }

        await Root.SelectComponentAsync(null);
    }

    /// <summary>
    /// Returns the CSS column width that must be used by a component in the editor
    /// to reflect its width and the currently selected resolution.
    /// </summary>
    /// <param name = "componentsInRow"></param>
    /// <returns></returns>
    private int CalculateRowSize(RowDto componentsInRow)
    {
        int size = 0;
        foreach (var component in componentsInRow.ComponentList)
        {
            size += ComponentUtils.CalculateColumnWidth(component);
        }

        return size;
    }
    #endregion Helper Methods
}


